/******************************************************************************
* 	Copyright (c) 2007-2008 All rights reserved
*		Asemantics S.r.l
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer. 
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by 
*	 Asemantics S.r.l."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*    This product includes software developed by Asemantics S.r.l.
*    the Semantic Web company, Rome, London, Leiden and its contributors. 
*
* 5. Neither the name of the company nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Andrea Marchesini - baku@asemantics.com
*
******************************************************************************/

#include <log2html.h>
#include <analyze.h>
#include <configure.h>

#include <string.h>

static gboolean analyze_startup (struct data_t *data, tb_json_object_t * json,
				 gdouble time, GError ** error);
static gboolean analyze_quit (struct data_t *data, tb_json_object_t * json,
			      gdouble time, GError ** error);
static gboolean analyze_istance_born (struct data_t *data,
				      tb_json_object_t * json, gdouble time,
				      GError ** error);
static gboolean analyze_istance_error (struct data_t *data,
				       tb_json_object_t * json, gdouble time,
				       GError ** error);
static gboolean analyze_istance_wait (struct data_t *data,
				      tb_json_object_t * json, gdouble time,
				      GError ** error);
static gboolean analyze_istance_died (struct data_t *data,
				      tb_json_object_t * json, gdouble time,
				      GError ** error);

gboolean
analyze (struct data_t *data, gchar * line, gsize size, GError ** error)
{
  tb_json_t *json;
  tb_json_object_t *object;

  tb_json_node_t *node;
  tb_json_value_t *value;
  gdouble time;
  gchar *type;

  gboolean ret;

  json = tb_json_new ();

  /* Load the json object from the file: */
  if (tb_json_load_from_buffer (json, line, size, error) == FALSE)
    {
      tb_json_destroy (json);
      return FALSE;
    }

  /* A stupid check: */
  if (tb_json_is_object (json) == FALSE)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The argument must be an object!");
      tb_json_destroy (json);
      return FALSE;
    }

  object = tb_json_object (json);

  /* Getting the time: */
  if (!(node = tb_json_object_get_node (object, "time")))
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The object is not valid (no time node)!");
      tb_json_destroy (json);
      return FALSE;
    }

  /* Validate the time: */
  value = tb_json_node_get_value (node);

  if (tb_json_value_get_type (value) != TB_JSON_VALUE_NUMBER)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The object is not valid (the time node must be a number)!");
      tb_json_destroy (json);
      return FALSE;
    }

  time = tb_json_value_get_number (value);

  /* Getting the type: */
  if (!(node = tb_json_object_get_node (object, "type")))
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The object is not valid (no type node)!");
      tb_json_destroy (json);
      return FALSE;
    }

  /* Validate the type: */
  value = tb_json_node_get_value (node);

  if (tb_json_value_get_type (value) != TB_JSON_VALUE_STRING)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The object is not valid (the type node must be a string)!");
      tb_json_destroy (json);
      return FALSE;
    }

  type = tb_json_value_get_string (value);

  if (!strcmp (type, "STARTUP"))
    ret = analyze_startup (data, object, time, error);

  else if (!strcmp (type, "QUIT"))
    ret = analyze_quit (data, object, time, error);

  else if (!strcmp (type, "ISTANCE_BORN"))
    ret = analyze_istance_born (data, object, time, error);

  else if (!strcmp (type, "ISTANCE_ERROR"))
    ret = analyze_istance_error (data, object, time, error);

  else if (!strcmp (type, "ISTANCE_WAIT"))
    ret = analyze_istance_wait (data, object, time, error);

  else if (!strcmp (type, "ISTANCE_DIED"))
    ret = analyze_istance_died (data, object, time, error);

  else
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The type '%s' is unknown", type);
      ret = FALSE;
    }

  /* Bye: */
  g_free (type);
  tb_json_destroy (json);

  return ret;
}

/* LOG NEW ******************************************************************/

static void
analyze_log_new (struct data_t *data)
{
  struct log_t *log;

  log = g_malloc0 (sizeof (struct log_t));
  data->logs = g_list_append (data->logs, log);
  data->current_log = log;
}

/* GENERIC ANALYZE FUNCTIONS ***********************************************/

static gint
analyze_json_get_gint (tb_json_object_t * json, gchar * node_str)
{
  tb_json_node_t *node;
  tb_json_value_t *value;

  if (!(node = tb_json_object_get_node (json, node_str)))
    return 0;

  value = tb_json_node_get_value (node);

  if (tb_json_value_get_type (value) != TB_JSON_VALUE_NUMBER)
    return 0;

  return (gint) tb_json_value_get_number (value);
}

static gchar *
analyze_json_get_gchar (tb_json_object_t * json, gchar * node_str)
{
  tb_json_node_t *node;
  tb_json_value_t *value;

  if (!(node = tb_json_object_get_node (json, node_str)))
    return NULL;

  value = tb_json_node_get_value (node);

  if (tb_json_value_get_type (value) != TB_JSON_VALUE_STRING)
    return NULL;

  return tb_json_value_get_string (value);
}

static gboolean
analyze_json_get_gboolean (tb_json_object_t * json, gchar * node_str)
{
  tb_json_node_t *node;
  tb_json_value_t *value;

  if (!(node = tb_json_object_get_node (json, node_str)))
    return FALSE;

  value = tb_json_node_get_value (node);

  if (tb_json_value_get_type (value) != TB_JSON_VALUE_BOOLEAN)
    return FALSE;

  return tb_json_value_get_boolean (value);
}

/* TYPE FUNCTIONS ***********************************************************/

/* This code creates a new log system: */
static gboolean
analyze_startup (struct data_t *data, tb_json_object_t * json, gdouble time,
		 GError ** error)
{
  gint pid;

  /* This code creates ALWAYS a new log: */
  analyze_log_new (data);

  data->current_log->startup = time;

  if ((pid = analyze_json_get_gint (json, "pid")))
    data->current_log->pid = pid;

  return TRUE;
}

static gboolean
analyze_quit (struct data_t *data, tb_json_object_t * json, gdouble time,
	      GError ** error)
{
  if (!data->current_log)
    analyze_log_new (data);

  data->current_log->quit = time;

  /* This log removes always the current log: */
  data->current_log = NULL;
  return TRUE;
}

static gboolean
analyze_istance_wait (struct data_t *data, tb_json_object_t * json,
		      gdouble time, GError ** error)
{
  struct log_wait_t *wait;

  if (!data->current_log)
    analyze_log_new (data);

  wait = g_malloc0 (sizeof (struct log_wait_t));
  wait->time = time;

  data->current_log->wait_istances =
    g_list_append (data->current_log->wait_istances, wait);

  return TRUE;
}

static struct log_istance_t *
analyze_istance (struct data_t *data, tb_json_object_t * json, gdouble time,
		 GError ** error)
{
  struct log_istance_t *istance;
  gchar *process;
  gboolean meta;
  gint pid;

  if (!(pid = analyze_json_get_gint (json, "istance")))
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "The istance doesn't contain pid!");
      return NULL;
    }

  if (!data->current_log)
    analyze_log_new (data);

  istance = g_malloc0 (sizeof (struct log_istance_t));
  data->current_log->istances =
    g_list_append (data->current_log->istances, istance);

  if ((process = analyze_json_get_gchar (json, "process")))
    istance->process = process;

  if ((meta = analyze_json_get_gboolean (json, "process_meta")))
    istance->process_meta = meta;

  istance->time = time;
  istance->pid = pid;

  return istance;
}

static gboolean
analyze_istance_born (struct data_t *data, tb_json_object_t * json,
		      gdouble time, GError ** error)
{
  struct log_istance_t *istance;
  gchar *input;
  gboolean inl;
  gint parent;

  /* Create a new istance: */
  if (!(istance = analyze_istance (data, json, time, error)))
    return FALSE;

  istance->type = ISTANCE_BORN;

  if ((input = analyze_json_get_gchar (json, "input")))
    istance->born.input = input;

  if ((inl = analyze_json_get_gboolean (json, "inline")))
    istance->born.inl = inl;

  if ((parent = analyze_json_get_gint (json, "parent")))
    istance->born.parent = parent;

  return TRUE;
}

static gboolean
analyze_istance_error (struct data_t *data, tb_json_object_t * json,
		       gdouble time, GError ** error)
{
  struct log_istance_t *istance;
  gchar *tmp;

  /* Create a new istance: */
  if (!(istance = analyze_istance (data, json, time, error)))
    return FALSE;

  if ((tmp = analyze_json_get_gchar (json, "reason")))
    {
      istance->error_exec.reason = tmp;

      if ((tmp = analyze_json_get_gchar (json, "command")))
	istance->error_exec.command = tmp;

      istance->type = ISTANCE_ERROR_EXEC;
      return TRUE;
    }

  if ((tmp = analyze_json_get_gchar (json, "error_message")))
    {
      istance->error_output = tmp;

      istance->type = ISTANCE_ERROR_OUTPUT;
      return TRUE;
    }

  if ((tmp = analyze_json_get_gchar (json, "operation")))
    {
      istance->error_io.operation = tmp;

      if ((tmp = analyze_json_get_gchar (json, "error")))
	istance->error_io.error = tmp;

      istance->type = ISTANCE_ERROR_IO;
      return TRUE;
    }

  if ((tmp = analyze_json_get_gchar (json, "process_error")))
    {
      istance->error_process.process = tmp;

      if ((tmp = analyze_json_get_gchar (json, "error")))
	istance->error_process.error = tmp;

      istance->type = ISTANCE_ERROR_PROCESS;
      return TRUE;
    }

  if ((tmp = analyze_json_get_gchar (json, "parser_error")))
    {
      istance->error_parser = tmp;

      istance->type = ISTANCE_ERROR_PARSER;
      return TRUE;
    }

  data->current_log->istances =
    g_list_remove (data->current_log->istances, istance);
  configure_free_istance (istance);

  g_set_error (error, log2html_error_quark (), 0, "Unknown error type!");
  return FALSE;
}

static gboolean
analyze_istance_died (struct data_t *data, tb_json_object_t * json,
		      gdouble time, GError ** error)
{
  struct log_istance_t *istance;
  gchar *output;
  gint status;

  /* Create a new istance: */
  if (!(istance = analyze_istance (data, json, time, error)))
    return FALSE;

  istance->type = ISTANCE_DIED;

  if ((status = analyze_json_get_gint (json, "status")))
    istance->died.status = status;

  if ((output = analyze_json_get_gchar (json, "output")))
    istance->died.output = output;

  return TRUE;
}

/* EOF */
